﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PersonalAlbum.Data.Objects;

namespace PersonalAlbum.Data.Entities
{
    public class EntryRepository : IEntryRepository
    {

        private string _connectionString;
        public EntryRepository(string connectionString)
        {
            this._connectionString = connectionString;
        }

        /// <summary>
        /// Add a new tag if the tag does not exist, else increase it's count. 
        /// Add a new relation if non was found.
        /// </summary>
        /// <param name="entryId">The entry id</param>
        /// <param name="tags">The tag name</param>   
        public void AddTagsForEntry(int entryId, string tagName)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                ImagesDal.Tag t = ImagesDB.Tag.FirstOrDefault(s => s.Name == tagName.Trim());
                if (t == null)
                {
                    t = new ImagesDal.Tag();
                    t.Name = tagName.Trim();
                    t.Count = 0;
                    ImagesDB.AddToTag(t);
                    ImagesDB.SaveChanges();//so we'll get a tag id
                }
                t.Count++;
                ImagesDal.TagEntryRelation te = ImagesDB.TagEntryRelation.FirstOrDefault(s => s.TagId == t.Id && s.EntryId == entryId);
                if (te == null)
                {
                    te = new ImagesDal.TagEntryRelation() { EntryId = entryId, TagId = t.Id };
                    ImagesDB.AddToTagEntryRelation(te);
                }
                ImagesDB.SaveChanges();
            }
        }


        public List<PersonalAlbum.Data.Objects.Entry> GetItems()
        {
            return GetItems(1, int.MaxValue, true);
        }

        public List<PersonalAlbum.Data.Objects.Entry> GetItems(int pageNumber, int count, bool visible)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                var entriesResult = LoadItems(ImagesDB.Entry.Where(e => (visible ? e.visible : true)).Skip((pageNumber - 1) * count).Take(count),true);
                return entriesResult.ToList();
            }
        }

        /// <summary>
        /// Load random visible entries summary (no replies or tags)
        /// </summary>
        /// <param name="count">The max number of entries to load</param>
        /// <returns>List of Entry</returns>
        public List<PersonalAlbum.Data.Objects.Entry> LoadRandomEntriesSummary(int count)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                if (count <= 0)
                {
                    return new List<PersonalAlbum.Data.Objects.Entry>();
                }
                else
                {
                    var entries = ImagesDB.Entry.Where(e => e.visible);
                    int maxSkip = Math.Max(0, entries.Count() - count);
                    return (LoadItems(entries.Skip(new Random().Next(maxSkip)).Take(count), false).ToList());
                }
            }
        }

        private IEnumerable<PersonalAlbum.Data.Objects.Entry> LoadItems(IQueryable<ImagesDal.Entry> objectQuery, bool withTagsAndReplies)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                return objectQuery.Select(e =>
                    new PersonalAlbum.Data.Objects.Entry()
                    {
                        AlbumId = e.AlbumId.GetValueOrDefault(),
                        Description = e.Description,
                        EntryType = e.EntryType.GetValueOrDefault(),
                        FileSize = e.FileSize.GetValueOrDefault(),
                        Height = e.Height.GetValueOrDefault(),
                        Id = e.Id,
                        Title = e.Title,
                        Uri = e.Uri,
                        VideoLen = e.VideoLen.GetValueOrDefault(),
                        Visible = e.visible,
                        Width = e.Width.GetValueOrDefault(),
                        Date = e.Date,
                        ThumbHeight = e.ThumbHeight.GetValueOrDefault(),
                        ThumbUri = e.ThumbURI,
                        ThumbWidth = e.ThumbWidth.GetValueOrDefault(),
                        Tags = from r in ImagesDB.TagEntryRelation
                               join t in ImagesDB.Tag on r.TagId equals t.Id
                               where r.EntryId == e.Id
                               select new Tag()
                               {
                                   Count = t.Count,
                                   Id = t.Id,
                                   Name = t.Name
                               },
                        Replies = from r in ImagesDB.Reply
                                  where r.EntryId == e.Id
                                  select new Reply()
                                  {
                                      Id = r.Id,
                                      Email = r.Email,
                                      EntryId = r.EntryId,
                                      Body = r.Body,
                                      IsApproved = r.Approved,
                                      Subject = r.Subject,
                                      TimeStamp = r.Timestamp,
                                      UserName = r.Username
                                  }

                    });
            }

        }


        public List<Entry> GetEntriesByTagName(string tagName, bool visible)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                var entries = ImagesDB.Entry;
                var relations = ImagesDB.TagEntryRelation;
                var tags = ImagesDB.Tag;
                var entriesResult = LoadItems(from r in relations
                                              join e in entries on r.EntryId equals e.Id
                                              join t in tags on r.TagId equals t.Id
                                              where t.Name.Contains(tagName.Trim()) && (visible ? e.visible : true)
                                              select e,true);
                return entriesResult.ToList();
            }
        }

        public void AddItem(Entry item)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                ImagesDal.Entry e = new ImagesDal.Entry()
                {
                    Width = item.Width,
                    visible = item.Visible,
                    VideoLen = item.VideoLen,
                    Uri = item.Uri,
                    Title = item.Title,
                    Height = item.Height,
                    FileSize = item.FileSize,
                    EntryType = item.EntryType,
                    Description = item.Description,
                    AlbumId = item.AlbumId,
                    Date = item.Date,
                    ThumbWidth = item.ThumbWidth,
                    ThumbURI = item.ThumbUri,
                    ThumbHeight = item.ThumbHeight
                };
                ImagesDB.AddToEntry(e);
                ImagesDB.SaveChanges();
                item.Id = e.Id;
                AddTags(item);
            }
        }

        private void AddTags(PersonalAlbum.Data.Objects.Entry item)
        {
            if (item.Tags != null)
            {
                foreach (var tag in item.Tags)
                {
                    AddTagsForEntry(item.Id, tag.Name);
                }
            }
        }

        public void DeleteItem(PersonalAlbum.Data.Objects.Entry item)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                var old = ImagesDB.Entry.FirstOrDefault(e => e.Id == item.Id);
                if (old != null)
                {
                    ImagesDB.DeleteObject(old);
                    RemoveTagsForEntry(item.Id);
                    ImagesDB.SaveChanges();

                }
            }
        }

        /// <summary>
        /// Removes all of the relations of the specified entry, 
        /// and decreases the count all of the relevant tags
        /// </summary>
        /// <param name="entryId">The entry id</param>
        public void RemoveTagsForEntry(int entryId)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                var relations = ImagesDB.TagEntryRelation.Where(s => s.EntryId == entryId);
                foreach (var item in relations)
                {
                    var t = ImagesDB.Tag.FirstOrDefault(s => s.Id == item.TagId);
                    if (t != null)
                    {
                        if (t.Count > 1)
                        {
                            t.Count--;
                        }
                        else
                        {
                            ImagesDB.DeleteObject(t);
                        }
                    }
                    ImagesDB.DeleteObject(item);
                }
            }
        }

        public void UpdateItem(PersonalAlbum.Data.Objects.Entry item)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                var old = ImagesDB.Entry.FirstOrDefault(e => e.Id == item.Id);
                if (old != null)
                {
                    old.Description = item.Description;
                    old.visible = item.Visible;
                    old.AlbumId = item.AlbumId;
                    old.EntryType = item.EntryType;
                    old.FileSize = item.FileSize;
                    old.Height = item.Height;
                    old.Title = item.Title;
                    old.Uri = item.Uri;
                    old.VideoLen = item.VideoLen;
                    old.Width = item.Width;
                    old.ThumbHeight = item.ThumbHeight;
                    old.ThumbWidth = item.ThumbWidth;
                    old.ThumbURI = item.ThumbUri;

                    RemoveTagsForEntry(old.Id);
                    AddTags(item);

                    ImagesDB.SaveChanges();
                }
            }

        }
        public PersonalAlbum.Data.Objects.Entry GetItemById(int entryId)
        {
            return this.GetEntryById(entryId, true);
        }

        public PersonalAlbum.Data.Objects.Entry GetEntryById(int entryId, bool visible)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                return (ImagesDB.Entry.Where(e => e.Id == entryId && (visible ? e.visible : true)).Select(e =>
                    new PersonalAlbum.Data.Objects.Entry()
                    {
                        AlbumId = e.AlbumId.GetValueOrDefault(),
                        Description = e.Description,
                        EntryType = e.EntryType.GetValueOrDefault(),
                        FileSize = e.FileSize.GetValueOrDefault(),
                        Height = e.Height.GetValueOrDefault(),
                        Id = e.Id,
                        Title = e.Title,
                        Uri = e.Uri,
                        VideoLen = e.VideoLen.GetValueOrDefault(),
                        Visible = e.visible,
                        Width = e.Width.GetValueOrDefault(),
                        Date = e.Date,
                        ThumbHeight = e.ThumbHeight.GetValueOrDefault(),
                        ThumbUri = e.ThumbURI,
                        ThumbWidth = e.ThumbWidth.GetValueOrDefault(),
                        Tags = from r in ImagesDB.TagEntryRelation
                               join t in ImagesDB.Tag on r.TagId equals t.Id
                               where r.EntryId == e.Id
                               select new PersonalAlbum.Data.Objects.Tag()
                               {
                                   Count = t.Count,
                                   Id = t.Id,
                                   Name = t.Name
                               },
                        Replies = from r in ImagesDB.Reply
                                  where r.EntryId == e.Id
                                  select new PersonalAlbum.Data.Objects.Reply()
                                  {
                                      Id = r.Id,
                                      Email = r.Email,
                                      EntryId = r.EntryId,
                                      Body = r.Body,
                                      IsApproved = r.Approved,
                                      Subject = r.Subject,
                                      TimeStamp = r.Timestamp,
                                      UserName = r.Username
                                  }

                    })).SingleOrDefault();
            }

        }
        public List<PersonalAlbum.Data.Objects.Entry> GetEntriesByAlbum(int albumId, int pageNumber, int count, bool visible)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                return LoadItems(ImagesDB.Entry.Where(e => e.AlbumId == albumId && (visible ? e.visible : true)).Skip((pageNumber - 1) * count).Take(count),true).ToList();
            }
        }


        #region IEntryRepository Members


        public int GetEntriesCount(bool visible)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                if (visible)
                {
                    return ImagesDB.Entry.Count(e => e.visible);
                }
                else
                {
                    return ImagesDB.Entry.Count();
                }
            }
        }

        #endregion

        #region IEntryRepository Members

        /// <summary>
        /// Load the latest entries
        /// </summary>
        /// <param name="count">The number of entries to load</param>
        /// <returns>List of Entry</returns>
        public List<PersonalAlbum.Data.Objects.Entry> GetLatestEntries(int count, bool visible)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                return (from e in ImagesDB.Entry
                        where (visible ? e.visible : true)
                        orderby e.Id descending
                        select new PersonalAlbum.Data.Objects.Entry()
                        {
                            AlbumId = e.AlbumId.GetValueOrDefault(),
                            Description = e.Description,
                            EntryType = e.EntryType.GetValueOrDefault(),
                            FileSize = e.FileSize.GetValueOrDefault(),
                            Height = e.Height.GetValueOrDefault(),
                            Id = e.Id,
                            Title = e.Title,
                            Uri = e.Uri,
                            VideoLen = e.VideoLen.GetValueOrDefault(),
                            Visible = e.visible,
                            Width = e.Width.GetValueOrDefault(),
                            Date = e.Date,
                            ThumbHeight = e.ThumbHeight.GetValueOrDefault(),
                            ThumbUri = e.ThumbURI,
                            ThumbWidth = e.ThumbWidth.GetValueOrDefault(),
                            Tags = from r in ImagesDB.TagEntryRelation
                                   join t in ImagesDB.Tag on r.TagId equals t.Id
                                   where r.EntryId == e.Id
                                   select new PersonalAlbum.Data.Objects.Tag()
                                   {
                                       Count = t.Count,
                                       Id = t.Id,
                                       Name = t.Name
                                   },
                            Replies = from r in ImagesDB.Reply
                                      where r.EntryId == e.Id
                                      select new PersonalAlbum.Data.Objects.Reply()
                                      {
                                          Id = r.Id,
                                          Email = r.Email,
                                          EntryId = r.EntryId,
                                          Body = r.Body,
                                          IsApproved = r.Approved,
                                          Subject = r.Subject,
                                          TimeStamp = r.Timestamp,
                                          UserName = r.Username
                                      }
                        }).Take(count).ToList();
            }
        }


        public PersonalAlbum.Data.Objects.Entry GetLatestEntry(bool visible)
        {
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                return (ImagesDB.Entry.Where(e => (visible ? e.visible : true)).Select(e =>
                    new PersonalAlbum.Data.Objects.Entry()
                    {
                        AlbumId = e.AlbumId.GetValueOrDefault(),
                        Description = e.Description,
                        EntryType = e.EntryType.GetValueOrDefault(),
                        FileSize = e.FileSize.GetValueOrDefault(),
                        Height = e.Height.GetValueOrDefault(),
                        Id = e.Id,
                        Title = e.Title,
                        Uri = e.Uri,
                        VideoLen = e.VideoLen.GetValueOrDefault(),
                        Visible = e.visible,
                        Width = e.Width.GetValueOrDefault(),
                        Date = e.Date,
                        ThumbHeight = e.ThumbHeight.GetValueOrDefault(),
                        ThumbUri = e.ThumbURI,
                        ThumbWidth = e.ThumbWidth.GetValueOrDefault(),
                        Tags = from r in ImagesDB.TagEntryRelation
                               join t in ImagesDB.Tag on r.TagId equals t.Id
                               where r.EntryId == e.Id
                               select new PersonalAlbum.Data.Objects.Tag()
                               {
                                   Count = t.Count,
                                   Id = t.Id,
                                   Name = t.Name
                               },
                        Replies = from r in ImagesDB.Reply
                                  where r.EntryId == e.Id
                                  select new PersonalAlbum.Data.Objects.Reply()
                                  {
                                      Id = r.Id,
                                      Email = r.Email,
                                      EntryId = r.EntryId,
                                      Body = r.Body,
                                      IsApproved = r.Approved,
                                      Subject = r.Subject,
                                      TimeStamp = r.Timestamp,
                                      UserName = r.Username
                                  }

                    })).LastOrDefault();
            }
        }

        public List<MonthSummary> GetEntriesByMonthsSummary(bool visible)
        {
            Func<ImagesDal.Entry, bool> filter;
            if (visible)
            {
                filter = e => (bool)e.visible;

            }
            else
            {
                filter = e => true;
            }
            using (var ImagesDB = new ImagesDal.SqlEntitiesProvider(this._connectionString))
            {
                return (from e in ImagesDB.Entry.Where(filter)
                        group e by
                        new { e.Date.Year, e.Date.Month } into g
                        orderby g.Key.Year, g.Key.Month
                        select new MonthSummary { Month = g.Key.Month, Year = g.Key.Year, Count = g.Count() }).ToList();
            }
        }

        #endregion
    }
}
